from optparse import OptionParser, Option, Values
from ConfigParser import ConfigParser
from warnings import warn
import re

"""Configuration for producing and consuming RDF/XML files.
See rdfxml.py and its "testconfig" section for usage examples.
This was originally from the 'envisat' package which needed to
constrain what predicates could be consumed.
More common usage should be simply to specify a list of 
namespaces and their aliases, without the predcate list.
"""

__all__ = ["RdfOptions", "RdfConfig", "RdfModel"]

cfgfile = Option("-c", "--config",
	help="config file [default /etc/envisat.cfg]",
	action="store",
	type="string",
	default="/etc/envisat.cfg"
)

class RdfOptions(OptionParser):
	standard_option_list = [cfgfile]

class RdfConfig(ConfigParser):
	## Override the regular expression in the parent class since we 
	## do not want : to be a key/value separator to permit using 
	## URLs as keys.
	OPTCRE = re.compile(
		r'(?P<option>[^=\s][^=]*)'	# very permissive!
		r'\s*(?P<vi>[=])\s*'		# any number of space/tab,
						# followed by separator
						# (=), followed
						# by any # space/tab
		r'(?P<value>.*)$'		# everything up to eol
	)

	def __init__(self, options):
		ConfigParser.__init__(self)
		cfgfile = options.config
		self.read([cfgfile])

	def has_predicate(self, node):
		"""
		Check if the given predicate is enabled in this configuration.
		This means that it must be present in the [predicates] section
		of the configuration file, or the "default" value in that section
		must be set to true.

		A predicate may be specified either as a complete URL, or else
		of the form prefix:rest where prefix is defined in the [namespaces]
		section of the config.

		The predicate must convert to a string URI when coerced to a string.
		This is true of strings themselves, obviously, as well as RDF.Uri
		objects.

		>>> from tempfile import mktemp
		>>> from os import unlink
		>>> options = Values({ 'config' : mktemp() })
		>>> fp = open(options.config, "w+")
		>>> fp.write(testconfig)
		>>> fp.close()
		>>> config = RdfConfig(options)
		>>> config.has_predicate("http://example.org/test#name")
		False
		>>> config.has_predicate("http://example.org/test#label")
		False
		>>> config.has_predicate("http://example.org/test#comment")
		True
		>>> config.has_predicate("http://example.org/test#nonexistent")
		True
		>>> unlink(options.config)
		"""
		uri = str(node)
		if self.has_option("predicates", uri):
			return self.getboolean("predicates", uri)
		for pfx, ns in self.items("namespaces"):
			if ns == uri[:len(ns)]:
				uri = pfx + ":" + uri[len(ns):]
				if self.has_option("predicates", uri):
					return self.getboolean("predicates", uri)
		if self.has_option("predicates", "default"):
			return self.getboolean("predicates", "default")
		return False

def RdfModel(config):
	"""
	Create an RDF.Model instance from storage parameters
	present in the configuration file. Arguments such as
	may be given to RDF.Storage should be present in the
	configuration in a section called [store].
	"""
	try:
		from RDF import Model, Storage
	except ImportError:
		warn("RDF not present", RuntimeWarning)
		return
	
	##
	## Defaults
	##
	storage_options = {
		"storage_name" : "memory",
		"name" : "memory",
		"options_string" : ""
	}

	if config.has_section("store"):
		for k in storage_options:
			if config.has_option("store", k):
				storage_options[k] = config.get("store", k)

	storage = Storage(**storage_options)
	model = Model(storage)
	return model

testconfig = """
[namespaces]
example=http://example.org/test#

[predicates]
default=true
example:name=false
http://example.org/test#label=false
example:comment=true
"""

if __name__ == '__main__':
	import doctest
	doctest.testmod()
